//============================================================================
// Copyright (C) 2010 Brett R. Jones
// All Rights Reserved
//
// You may redistribute and/or modify for non commercial and commercial uses
// provided this copyright notice remains in place and is not modified
//
// This code is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
//
// brettjones1900@gmail.com
// http://www.p2panarchy.com
//============================================================================
#include "DtwGame.h"





//------------------------------------------------------------------------------
//! startup with root directory of where to save databases and files
RCODE DtwSave::Startup( const char * pSaveRootDir )
{
	log_msg( 0, "DtwSave::Startup: start\n" );
	m_strRootPath = pSaveRootDir;
	VxMakeDirectory( m_strRootPath.c_str() );
	m_strSavesPath = m_strRootPath + "saves/";
	VxMakeDirectory( m_strSavesPath.c_str() );
	m_strScreenshotsPath = m_strRootPath + "screenshots/";
	VxMakeDirectory( m_strScreenshotsPath.c_str() );

	// config and stats files
	m_strConfigFile = m_strSavesPath + irr::core::stringc("config.xml");
	m_strStatsFile = m_strSavesPath + irr::core::stringc("stats.dat");

	// create database if doesnt exist
	m_poDatabase = new DtwDatabase();
	irr::core::stringc	strDatabase = m_strSavesPath + "storage.db3";
	m_poDatabase->DbStartup( strDatabase.c_str() );

	// get version from database
	m_s32DatabaseVersion = m_poDatabase->GetDatabaseVersion();
	if( m_s32DatabaseVersion > 0 )
	{
		log_msg( 0, "DtwSave::Startup: done\n" );
		return 0;
	}
	log_msg( 0, "DtwSave::Startup: ERROR.. Database Version %d\n", m_s32DatabaseVersion );
	return -1;
}

//------------------------------------------------------------------------------
//! shutdown
void DtwSave::Shutdown( void )
{
	if( m_poDatabase )
	{
		delete m_poDatabase;
		m_poDatabase = 0;
	}
}

//------------------------------------------------------------------------------
//! loads all level stats from database
RCODE DtwSave::LoadLevelStatsFromDb( void ) 
{
	log_msg( 0, "DtwSave::LoadLevelStatsFromDb: start db=%d\n", m_poDatabase );
	m_aoLevelStats.clear();
	char as8Buf[256];

	// select stats from db table
	RCODE rc = m_poDatabase->DbOpen();
	if( rc )
	{
		log_msg( 0, "DtwSave::LoadLevelStatsFromDb: error %d opening db\n", rc );
		return rc;
	}
	log_msg( 0, "DtwSave::LoadLevelStatsFromDb: Query..");
	m_poDatabase->RunQuery("PRAGMA journal_mode = 'OFF'");
	m_poDatabase->RunQuery("BEGIN TRANSACTION");
	// start querry of level stat rows from db
	m_poDatabase->StartDataQuery("SELECT * FROM Stats");

	// load stats from each db row
	while( 0 == m_poDatabase->GetDbRow() ) 
	{
		SaveLevelStats oLevelStat;
	
		// Get record data
		oLevelStat.m_u32StatsId		= m_poDatabase->GetDbS32(0);
		oLevelStat.m_strLevelName	= m_poDatabase->GetDbString(1);
		oLevelStat.m_s32Difficulty	= m_poDatabase->GetDbS32(2);
		oLevelStat.m_bUnlocked		= m_poDatabase->GetDbS32(3);
		oLevelStat.m_u32PlayTimeMs	= m_poDatabase->GetDbS32(4);
		oLevelStat.m_u32WinCount	= m_poDatabase->GetDbS32(5);
		oLevelStat.m_u32LoseCount	= m_poDatabase->GetDbS32(6);
		oLevelStat.m_u32LoadCount	= m_poDatabase->GetDbS32(7);


		// get highscores fpr this level
		sprintf( as8Buf, "SELECT * FROM HighScores where StatsId = %d", oLevelStat.m_u32StatsId );
		m_poDatabase->StartDataQuery( as8Buf, 1 );
		while( 0 == m_poDatabase->GetDbRow(1) ) 
		{
			HighScoreInfo oHighScore;
			oHighScore.m_u32PlayTimeMs		= m_poDatabase->GetDbS32( 2, 1 );
			oHighScore.m_u32UnixDateStamp	= m_poDatabase->GetDbS32( 3, 1 );
			oLevelStat.m_aoHighScores.push_back( oHighScore );
		}
		// Add record to map
		m_aoLevelStats[ oLevelStat.m_strLevelName ] = oLevelStat;
	}
	log_msg( 0, "DtwSave::LoadLevelStatsFromDb: closing..");

	// Close query
	m_poDatabase->RunQuery("END TRANSACTION");
	m_poDatabase->CloseQuery();
	m_poDatabase->DbClose();
	log_msg( 0, "DtwSave::LoadLevelStatsFromDb: done");


	return 0;
}

//------------------------------------------------------------------------------
//! save level stats for the given level
void DtwSave::SaveLevelStatsToDb( irr::core::stringc& strLevelName ) 
{
	LevelStatsIterator = m_aoLevelStats.find( strLevelName );
	if( LevelStatsIterator == m_aoLevelStats.end( ))
	{
		return;
	}

	// Get stat from map
	SaveLevelStats &oLevelStat = LevelStatsIterator->second;

	// Update database
	char as8Query[512];
	sprintf( as8Query, "UPDATE oStats SET "
					"PlayTimeMs = %d"
					", WinCount = %d"
					", LoseCount = %d"
					", LoadCount = %d"
					" WHERE ID = %d",
					oLevelStat.m_u32PlayTimeMs,
					oLevelStat.m_u32WinCount,
					oLevelStat.m_u32LoseCount,
					oLevelStat.m_u32LoadCount,
					oLevelStat.m_u32StatsId );

	m_poDatabase->RunQuery(as8Query);	
}

//------------------------------------------------------------------------------
//! update level stats for the given level
void DtwSave::UpdateLevelStats(  irr::core::stringc& strLevelName,  SaveLevelStats& oLevelStats )
{
	m_aoLevelStats[ strLevelName ] = oLevelStats;
}

//------------------------------------------------------------------------------
//! get level stats for the given level
RCODE DtwSave::GetLevelStats(	 irr::core::stringc& strLevelName, SaveLevelStats& oRetLevelStats )
{
	if( m_aoLevelStats.find( strLevelName ) == m_aoLevelStats.end() )
	{
		// not found
		log_msg( 0, "DtwSave::GetLevelStats: level %s not found\n", strLevelName.c_str() );
		return -1;
	}
	oRetLevelStats = m_aoLevelStats[ strLevelName ];
	return 0;
}

//------------------------------------------------------------------------------
//! get a pointer level stats for the given level
const SaveLevelStats * DtwSave::GetLevelStats(  irr::core::stringc& strLevelName )
{
	if( m_aoLevelStats.find( strLevelName ) == m_aoLevelStats.end() )
	{
		// not found
		log_msg( 0, "DtwSave::GetLevelStats: level %s not found\n", strLevelName.c_str() );
		return NULL;
	}
	return &m_aoLevelStats[ strLevelName ];
}

//------------------------------------------------------------------------------
//! add a score for this level 
void DtwSave::AddScore(  irr::core::stringc& strLevelName, U32 u32PlayTimeMs )
{
	// get level stats
	SaveLevelStats oStats;
	if( GetLevelStats( strLevelName, oStats) )
	{
		log_msg( 0, "DtwSave::AddScore: error getting level %s\n", strLevelName.c_str() );
		return;
	}

	// add score in lowest play time sorted order
	int iInsertIdx = -1;
	if( 0 == oStats.m_aoHighScores.size() ) 
	{
		// no high scores yet so idx is zero
		iInsertIdx = 0;
	}
	else 
	{
		// if room in the list or better than a existing score then find insert idx
		for( U32 i = 0; i < oStats.m_aoHighScores.size(); i++ ) 
		{
			if( u32PlayTimeMs < oStats.m_aoHighScores[i].m_u32PlayTimeMs ) 
			{
				// better score than existing
				iInsertIdx = i;
				break;
			}
		}
		if( ( -1 == iInsertIdx ) && 
			( oStats.m_aoHighScores.size() < MAX_SCORES_IN_STATS_LIST )  )
		{
			// room in list so insert at tail
			iInsertIdx = oStats.m_aoHighScores.size();
		}
	}

	if( -1 != iInsertIdx ) 
	{
		// found a place to insert so do so
		oStats.m_aoHighScores.insert( HighScoreInfo( u32PlayTimeMs, (int)time(NULL)), iInsertIdx );

		// if list is too long then remove a score
		if( oStats.m_aoHighScores.size() > MAX_SCORES_IN_STATS_LIST )
		{
			oStats.m_aoHighScores.erase( MAX_SCORES_IN_STATS_LIST, oStats.m_aoHighScores.size() - MAX_SCORES_IN_STATS_LIST );
		}
		// update persistent records
		UpdateLevelStats( strLevelName, oStats );

		m_poDatabase->DbOpen();
		m_poDatabase->RunQuery("BEGIN TRANSACTION");
		// delete old scores
		char as8Query[256];
		sprintf( as8Query, "DELETE FROM HighScores WHERE StatsId = %d", oStats.m_u32StatsId );
		m_poDatabase->RunQuery( as8Query );

		// insert new scores
		for( irr::u32 i = 0; i < oStats.m_aoHighScores.size(); i++ ) 
		{
			sprintf(as8Query, "INSERT INTO HighScores(StatsId, PlayTimeMs, UnixDate) VALUES(%d, %d, %d)", oStats.m_u32StatsId, oStats.m_aoHighScores[i].m_u32PlayTimeMs, (int)oStats.m_aoHighScores[i].m_u32UnixDateStamp );
			m_poDatabase->RunQuery( as8Query );
		}

		m_poDatabase->RunQuery("END TRANSACTION");
		m_poDatabase->DbClose();
	}
}

//------------------------------------------------------------------------------
//! add to count of times user loaded the level
void DtwSave::IncrementLevelLoadCount( irr::core::stringc& strLevelName ) 
{
	SaveLevelStats oStats;
	GetLevelStats( strLevelName, oStats );

	oStats.m_u32LoadCount++;
	UpdateLevelStats( strLevelName, oStats );
}

//------------------------------------------------------------------------------
//! add to count of times user lost the level
void DtwSave::IncrementLevelLoseCount( irr::core::stringc& strLevelName ) 
{
	SaveLevelStats oStats;
	GetLevelStats( strLevelName, oStats );

	oStats.m_u32LoseCount++;
	UpdateLevelStats( strLevelName, oStats );
}

//------------------------------------------------------------------------------
//! add to count of times user won the level
void DtwSave::IncrementLevelWinCount( irr::core::stringc& strLevelName ) 
{
	SaveLevelStats oStats;
	GetLevelStats( strLevelName, oStats );

	oStats.m_u32WinCount++;
	UpdateLevelStats( strLevelName, oStats );
}

//------------------------------------------------------------------------------
//! add to level play time
void DtwSave::IncrementLevelPlayTime( irr::core::stringc& strLevelName, U32 u32PlayTimeMs ) 
{
	SaveLevelStats oStats;
	GetLevelStats( strLevelName, oStats );

	oStats.m_u32PlayTimeMs += u32PlayTimeMs;
	UpdateLevelStats( strLevelName, oStats );
}

//------------------------------------------------------------------------------
//! unlock level
void DtwSave::UnlockLevel( irr::core::stringc& strLevelName ) 
{
	SaveLevelStats oStats;
	GetLevelStats( strLevelName, oStats );

	// set unlocked true
	oStats.m_bUnlocked = 1;

	// if level doesnt exist in database then insert
	char as8Query[512];
	sprintf( as8Query, "SELECT ID FROM oStats WHERE LevelFile = '%s'", strLevelName.c_str() );
	int iCount = m_poDatabase->QueryAndReturnFirstColumnInteger( as8Query );
	if( iCount == 0) 
	{
		sprintf( as8Query, "INSERT INTO oStats(LevelName, Unlocked, Difficulty) VALUES('%s', %d, %d)", strLevelName.c_str(), oStats.m_bUnlocked, 0);
		m_poDatabase->RunQuery( as8Query );
		oStats.m_u32StatsId = (S32)m_poDatabase->GetLastInsertId();
	}
	UpdateLevelStats( strLevelName, oStats );
}
